﻿''' <summary>
''' 后台上传历史数据
''' </summary>
''' <remarks></remarks>
Public Class BackgroundHistorydata

#Region "Single"

    Private Shared _instance As New BackgroundHistorydata

    Public Shared ReadOnly Property Instance() As BackgroundHistorydata
        Get
            Return _instance
        End Get
    End Property

    Sub New()
        If g_test Then
            halfPage1 = 255
            halfPage2 = 255
        End If

        _bw = New System.ComponentModel.BackgroundWorker()
        _bw.WorkerSupportsCancellation = True
        AddHandler _bw.DoWork, AddressOf DoWork
    End Sub

#End Region

    '历史数据的前半页长度（标志）
    Private halfPage1 As Byte = 1

    '历史数据的后半页长度（标志）
    Private halfPage2 As Byte = 2

    '后台上传的工作线程
    Private WithEvents _bw As System.ComponentModel.BackgroundWorker

    '历史数据上传完成时间
    Public Event HistoryDataUploadFinishedEvent As EventHandler


    '是否取消后台线程
    Private _cancel As Boolean = False

    Private _isStarting As Boolean = False
    ''' <summary>
    ''' 正在准备运行
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property isStarting() As Boolean
        Get
            Return _isStarting
        End Get
        Set(ByVal value As Boolean)
            _isStarting = value
        End Set
    End Property

    ''' <summary>
    ''' 开始执行后台上传
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub Start()

        If _isStarting = True Then  '已经启动线程开始过程
            Exit Sub
        End If

        '_isStarting置为True,表示准备上传
        _isStarting = True

        '等待上次线程的结束(最长等待2分钟)
        For i As Integer = 0 To 1200

            If _bw.IsBusy = False Then
                Exit For
            End If

            System.Threading.Thread.Sleep(100)
            Application.DoEvents()
            If _cancel = True Then
                _isStarting = False
                Exit Sub
            End If
        Next

        '_isStarting置为False,表示准备结束,进入上传状态
        _isStarting = False

        _cancel = False
        _bw.RunWorkerAsync()

    End Sub

    Public Sub StopRead()

        _cancel = True
        If _bw.IsBusy = True Then
            _bw.CancelAsync()
            Application.DoEvents()
        End If

    End Sub


    ''' <summary>
    ''' 后台线程的执行方法
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs)
        Debug.WriteLine("********** 开始历史data后台上传 " & Now.ToLongTimeString())

        '延迟10分钟,然后开始上传
        'System.Threading.Thread.Sleep(10 * 60 * 1000 / g_one1)
        For i As Integer = 0 To 10 * 60 / g_one1
            If _cancel Then
                '取消上传
                Exit Sub
                'Throw New UploadCancelException()
            End If

            System.Threading.Thread.Sleep(1000)
        Next


        Try
            If g_BackgroundUploadHistoryData Then
                '上传所有历史数据
                Upload()
            End If
        Catch ucex As UploadCancelException
            Debug.WriteLine(ucex.GetType().ToString())
        Catch ex As Exception
            Debug.WriteLine(ex.Message)
        End Try
    End Sub

    ''' <summary>
    ''' 历史数据上传结束 
    ''' </summary>
    ''' <remarks></remarks>
    Protected Sub OnUploadFinished()
        If Not _cancel Then
            RaiseEvent HistoryDataUploadFinishedEvent(Me, New EventArgs())
        End If
    End Sub

    ''' <summary>
    ''' 上传历史数据
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub Upload()

        Dim cur_netid As Byte
        Dim cur_Project As String = MainForm.pi.ProjectID

        For Each dti As DevTypeItem In MainForm.pi.DevTypeGroupList

            cur_netid = dti.NetId

            'If _bw.CancellationPending Then
            '    Debug.WriteLine("################" + _bw.GetHashCode().ToString() + " Exit0")
            '    Exit For
            'End If

            PowerTimeClass.ClearBuffer()

            WaitforBusy()  '未读取报警索引文件前可进行busy等待,而不需要退出

            '读取掉电现场数据的每个区域保存时间(年,月,日)
            Dim dic As IDictionary(Of Integer, DateTime)
            dic = PowerTimeClass.GetHistoryDataYearMonth(cur_netid)

            If dic Is Nothing Then
                Debug.WriteLine("dic = Nothing")
                'Throw New Exception(" 读取掉电现场数据失败")
                'Exit Sub
                Continue For
            End If

            WaitFor()

            Dim dt As DateTime

            Dim dtCurrent As DateTime
            dtCurrent = COMHelper.Instance.getCurrentTime(cur_netid)

            If dtCurrent = DateTime.MinValue Then
                '读取控制器时间失败
                dtCurrent = COMHelper.Instance.getCurrentTime(cur_netid)

                If dtCurrent = DateTime.MinValue Then
                    Continue For
                End If

            End If

            WaitFor()

            dtCurrent = dtCurrent.AddSeconds(-1 * HIST_SAVE_INTERVAL_TIME * HIST_COUNT_EACH_PAGE - 20)

            Dim pageIndex As Integer = 0

            '循环处理所有历史日期的数据

            For Each kv As KeyValuePair(Of Integer, DateTime) In dic
                pageIndex = kv.Key * HIST_PLANPAGECOUNT_EACHAREA  '区地址

                dt = kv.Value

                Dim dtUpdatedDate As DataTable

                '获取该天所有已经上传的历史数据的索引记录
                dtUpdatedDate = GetHistory(cur_Project, cur_netid, dt)


                '循环上传某天的所有历史数据
                For i As Integer = 0 To HIST_USEDPAGECOUNT_EACHAREA - 1

                    WaitFor()

                    dt = kv.Value.AddSeconds(i * HIST_SAVE_INTERVAL_TIME * HIST_COUNT_EACH_PAGE)

                    If dt > dtCurrent Then
                        '历史数据的时间不能超过子控的当前时间
                        Debug.WriteLine("dt > dtCurrent  (exit for)")
                        Exit For
                    End If

                    '是否在掉电期间
                    If PowerTimeClass.HistoryDataValidate(cur_netid, dt) = False Then
                        '掉电期间的无效数据，不上传
                        Continue For
                    End If

                    '是否已经上传
                    Dim filter As String
                    filter = String.Format("iHour={0} and iMinute={1} and iSecond={2}", dt.Hour, dt.Minute, dt.Second)
                    If dtUpdatedDate.Select(filter).Length = 0 Then       '历史数据未上传
                        '上传历史数据
                        uploadHour(cur_Project, cur_netid, pageIndex + i, dt)
                        System.Threading.Thread.Sleep(5000)
                    End If
                Next
            Next
        Next
    End Sub


    ''' <summary>
    ''' 上传指定设备、指定时间（年月日小时）的历史数据
    ''' </summary>
    ''' <param name="netID"></param>
    ''' <param name="indexPage"></param>
    ''' <param name="dt"></param>
    ''' <remarks></remarks>
    Private Sub uploadHour(ByVal projectID As String, ByVal netID As Byte, ByVal indexPage As Integer, ByVal dt As DateTime)
        Debug.WriteLine("************ indexPage=" & indexPage & "   " & Now.ToLongTimeString())

        Dim buf(34 * 15 - 1) As Byte

        Dim b1(17 * 15 - 1) As Byte

        '历史数据页的前255字节
        b1 = COMHelper.Instance.Send4Byte(netID, FunctionCode.HistoryByteRead, indexPage, halfPage1)
        If b1 Is Nothing Then
            Exit Sub
        End If
        b1.CopyTo(buf, 0)

        System.Threading.Thread.Sleep(100)

        If _cancel Then
            '取消上传
            Throw New UploadCancelException()
        End If

        '上半页数据长度
        Dim len As Integer = b1.Length

        '历史数据页的后255字节
        b1 = COMHelper.Instance.Send4Byte(netID, FunctionCode.HistoryByteRead, indexPage, halfPage2)
        If b1 Is Nothing Then
            Exit Sub
        End If
        b1.CopyTo(buf, len)

        HistoryDataClass.DealData(dt, buf, projectID, netID)
        'HistoryDataClass.DealFinish(projectID, dt, netID)

    End Sub

    ''' <summary>
    ''' 获取指定年月已经上传的历史数据的记录
    ''' </summary>
    ''' <param name="netID"></param>
    ''' <param name="dt"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function GetHistory(ByVal pid As String, ByVal netID As Integer, ByVal dt As DateTime) As DataTable
        Dim strSql As String
        strSql = String.Format("Select * From tbccHistorydata_{0} where netID=? and iYear=? and iMonth=? and iDay=?", Trim(pid).Replace("-", "_"))
        Dim p(3) As SQLite.SQLiteParameter
        For i As Integer = 0 To p.Length - 1
            p(i) = New SQLite.SQLiteParameter()
        Next
        p(0).Value = netID
        p(1).Value = dt.Year
        p(2).Value = dt.Month
        p(3).Value = dt.Day

        Dim ds As DataSet
        ds = SqlHelper.ExecuteDataset(strSql, p)

        Return ds.Tables(0)
    End Function

    ''' <summary>
    ''' 读取指定设备的当前历史数据的年月列表
    ''' </summary>
    ''' <param name="netID"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function GetYearMonths(ByVal netID As Byte) As Dictionary(Of Integer, Integer)
        Dim ymList As Dictionary(Of Integer, Integer)

        ymList = PowerTimeClass.GetHistoryDataYearMonth(netID)

        If ymList Is Nothing Then
            ymList = New Dictionary(Of Integer, Integer)
        End If

        Return ymList
    End Function

    ''' <summary>
    ''' 判断是否其它地方正在读取数据
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub WaitFor()

        '其它地方正在修改参数或者前台上传时取消后台上传
        If g_busy OrElse g_dbbackup Then
            '取消上传
            Throw New UploadCancelException()
        End If

        If _cancel Then
            '取消上传
            Throw New UploadCancelException()
        End If

    End Sub


    ''' <summary>
    ''' 暂停等待busy一段时间
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub WaitforBusy()

        If _cancel Then
            '取消上传
            Throw New UploadCancelException()
        End If

        '其它地方正在修改参数或者前台上传时暂停后台上传
        While (g_busy OrElse g_dbbackup)
            System.Threading.Thread.Sleep(100)

            If _cancel Then
                '取消上传
                Throw New UploadCancelException()
            End If

        End While

    End Sub



    ''' <summary>
    ''' 后台线程运行结束
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub _bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles _bw.RunWorkerCompleted
        OnUploadFinished()
    End Sub
End Class
